﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net;
using System.Web;
using System.Threading;
using System.IO;
using System.ComponentModel;

using Styx.CommonBot;

namespace rndbuddy
{
    public class WebClientEx : WebClient
    {
        public int Timeout { get; set; }

        protected override WebRequest GetWebRequest(Uri address)
        {
            var request = base.GetWebRequest(address);
            request.Timeout = Timeout;
            return request;
        }
    }

    static class server_pipe
    {
        public static string server = "https://rndbuddy.com/";
        public static string scr_server = "http://scrpipe.rndbuddy.com/";

        static string gw_welcome = "api/welcome/";
        static string gw_version = "api/version/";
        static string gw_api_validate = "api/validate/";
        static string gw_api_apply = "api/apply/";
        static string gw_api_apply_whatsapp = "api/apply/whatsapp/";
        static string gw_validate_code = "api/registration/validate/";
        static string gw_profile_generate = "api/profile";
        static string gw_profile_pipe = "api/profile/";
        static string gw_heartbeat = "api/heartbeat/";
        static string gw_cmdpipe = "api/command/pipe/";
        static string gw_profile_performance = "api/profile_performance/";
        static string gw_crowdsourcer = "api/crowdsourcer/report/";
        static string gw_toon_settings = "api/toon/settings/";
        static string gw_crowdsourcer_stuck = "api/crowdsourcer/stuck/";
        static string gw_target_reporter = "api/security/targeted/";
        static string gw_chat_log = "api/security/chat/";
        static string gw_kill_report = "api/security/kill/";
        static string gw_notification = "api/security/notify/";
        static string gw_toon_pb_settings = "api/toon/pb_settings/";
        static string gw_blackspot_report = "api/crowdsourcer/manual_blackspot/";

        public static string welcome_message()
        {
            string r = getStringFromWeb(server + gw_welcome);
            return r;
        }
        
        public static string server_version()
        {
            string result = HttpPost(server + gw_version, "");
            return result;
        }

        public static bool validate_api_key()
        {
			string result = HttpPost(server + gw_api_validate, "api_key=" + settings.api_key);
            if (result == "true")
                return true;
            return false;
        }

        public static bool validate_api_key(string apikey)
        {
            string result = HttpPost(server + gw_api_validate, "api_key=" + apikey);
            if (result == "true")
                return true;
            return false;
        }

        public static string apply_for_api_key(string email)
        {
            string result = HttpPost(server + gw_api_apply, "email=" + email);
            return result;
        }

        public static string[] apply_by_whatsapp(string email, string mobile)
        {
            string result = HttpPost(server + gw_api_apply_whatsapp, "email=" + email + "&mobile=" + mobile);
            return result.Split(';');
        }

        public static string validate_email(string code)
        {
            string result = HttpPost(server + gw_validate_code, "code=" + code);
            return result;
        }

        public static string[] request_new_profile()
        {
            string c_mapid = rndbuddy._toon.MapID;
            if (BotController.is_professionbuddy())
                c_mapid = ProfileHandler.pb_desired_zone;

            rndbuddy.setStatusText("Getting profile");
            string result = HttpPost(
                server + gw_profile_generate,
                    "api_key=" + settings.api_key +
                    "&version=" + settings.version.ToString() +
                    "&realm=" + rndbuddy._toon.Realm +
                    "&character=" + rndbuddy._toon.Name +
                    "&class=" + rndbuddy._toon.Class +
                    "&level=" + rndbuddy._toon.Level +
                    "&faction=" + rndbuddy._toon.FactionID + 
                    "&mapid=" + c_mapid +
                    "&zoneid=" + rndbuddy._toon.ZoneID +
                    "&x=" + rndbuddy._toon.x +
                    "&y=" + rndbuddy._toon.y +
                    "&z=" + rndbuddy._toon.z + 
                    "&botbase=" + TreeRoot.Current.Name + 
                    "&harvest_herb=" + settings.get_harvest_status("herb") + 
                    "&harvest_ore=" + settings.get_harvest_status("mine")
                    ,false,true
                    );

            string[] r = result.Split(';');
            return r;
        }

        public static string download_profile(string pid)
        {
            rndbuddy.setStatusText("Downloading profile");
            string result = HttpPost(server + gw_profile_pipe, "pid=" + pid);
            return result;
        }

        public static void heartbeat()
        {
            string result = HttpPost(server + gw_heartbeat,
                "apikey=" + settings.api_key +
                "&character=" + rndbuddy._toon.Name +
                "&level=" + rndbuddy._toon.Level +
                "&zone=" + rndbuddy._toon.ZoneText +
                "&zoneID=" + rndbuddy._toon.ZoneID +
                "&map=" + rndbuddy._toon.MapID +
                "&money=" + rndbuddy._toon.Money +
                "&bags_free=" + rndbuddy._toon.BagsFree +
                "&botbase=" + TreeRoot.Current.Name+
                "&botbase_status1=" + BotController.get_botbase_status()
                , true,false);

            string[] re = result.Split(';');
            if (re[0] == "false")
                rndbuddy.logsilent("Hearbeat Error: "+re[1]);
        }

        public static string get_pending_command()
        {
            string result = HttpPost(server + gw_cmdpipe,
                "apikey=" + settings.api_key +
                "&character=" + rndbuddy._toon.Name
                );
            
            string[] todo = result.Split(';');
            if (todo[0] == "false")
                return "null";
            return todo[1];
        }

        public static void report_profile_nph()
        {
			HttpPost(server + gw_profile_performance,
                "apikey=" + settings.api_key +
                "&character=" + rndbuddy._toon.Name +
                "&pid=" + ProfileHandler.current_profile +
				"&zoneName=" + ProfileHandler.profile_zone + 
                "&running=" + ProfileHandler.time_elapsed.ToString() +
                "&nph=" + ProfileHandler.nph.ToString()
				,true,false
                );
            return;
        }

        public static void report_crowdsourcer_data(bool reset_collection)
        {
            string data;
            data = crowdsourcer.merge_and_report(reset_collection);
            data = Crypto.EncodeTo64(data);

            HttpPost(server + gw_crowdsourcer,
                        "apikey=" + settings.api_key +
                        "&data="+data
						,true,false
                    );
        }

        public static string[] get_toon_settings()
        {
            rndbuddy.log("Fetching Toon Settings from Remote Server");

            string data = HttpPost(server+gw_toon_settings,
                    "apikey="+settings.api_key +
                    "&character=" + rndbuddy._toon.Name
                    );

            string[] result = data.Split(';');
            return result;
        }

        public static void report_stuck(float x, float y, float z)
        {
            HttpPost(server + gw_crowdsourcer_stuck,
                "apikey=" + settings.api_key +
                "&x=" + x.ToString() +
                "&y=" + y.ToString() +
                "&z=" + z.ToString() +
                "&mapID=" + rndbuddy._toon.MapID +
                "&zoneID=" + rndbuddy._toon.ZoneID
				,true,false
                );
            rndbuddy.log("Reported Position to Server");
        }

        public static void report_targeted(string name, int lvl, string faction, float x, float y, float z, double distance)
        {
            HttpPost(server + gw_target_reporter,
                "apikey=" + settings.api_key +
                "&char=" + rndbuddy._toon.Name +
                "&mapID=" + rndbuddy._toon.MapID +
                "&zoneID=" + rndbuddy._toon.ZoneID +
                "&zoneName=" + rndbuddy._toon.ZoneText +
                "&enemy_player=" + name +
                "&enemy_lvl=" + lvl +
                "&enemy_faction=" + faction +
                "&enemy_x=" + x.ToString() +
                "&enemy_y=" + y.ToString() +
                "&enemy_z=" + z.ToString() +
                "&enemy_distance=" + distance.ToString()
                , true, false
                );
        }

        public static void log_chat(string channel, string author, string message)
        {
            HttpPost(server + gw_chat_log,
                "apikey=" + settings.api_key +
                "&char=" + rndbuddy._toon.Name +
                "&mapID=" + rndbuddy._toon.MapID +
                "&zoneID=" + rndbuddy._toon.ZoneID +
                "&channel=" + channel +
                "&author=" + author +
                "&message=" + message
				,true,false
                );
        }

        public static void notify_kill(string reason = "")
        {
            if (String.IsNullOrEmpty(reason))
                reason = "No reason given!";

            HttpPost(server + gw_kill_report,
                "apikey=" + settings.api_key +
                "&char=" + rndbuddy._toon.Name +
                "&reason=" + reason
                ,true,false
                );
        }

        public static void notify(string message)
        {
            HttpPost(server + gw_notification,
                "apikey=" + settings.api_key +
                "&char=" + rndbuddy._toon.Name +
                "&reason=" + message
                ,true,false
                );
        }

        public static void uploadLatestScreenshot()
        {
            rndbuddy.log("uploading screenshot...");
            string scrpath = FSUtil.LatestFileInDir(settings.scrpath).FullName;
            string url = scr_server + "pipe/screenshot/" + rndbuddy._toon.Name + "/";
            
            //from main thread
            //UploadFile(url, scrpath);
            
            //make it delegate threaded
            Thread thread = new Thread(delegate() { UploadFile(url, scrpath); });
            thread.Start();
        }

        public static string[] get_toon_pb_settings()
        {
            rndbuddy.log("Fetching Professionbuddy Settings for this Toon from Remote Server");

            string data = HttpPost(server + gw_toon_pb_settings,
                    "apikey=" + settings.api_key +
                    "&character=" + rndbuddy._toon.Name
                    );

            string[] result = data.Split(';');
            return result;
        }

        public static void store_manual_blackspot(string mapID, string zoneID, string x, string y, string z, string r, string info)
        {
            rndbuddy.log("Submitting Manual Blackspot");
            HttpPost(server + gw_blackspot_report,
                "apikey=" + settings.api_key +
                "&character=" + rndbuddy._toon.Name +
                "&mapID=" + mapID +
                "&zoneID=" + zoneID +
                "&x=" + x +
                "&y=" + y +
                "&z=" + z +
                "&r=" + r +
                "&info=" + info
                );
        }

        public static string HttpPost(string URI, string Parameters, bool runInThread = false, bool readResponse = true)
        {
            if (runInThread)
            {
                Thread thread = new Thread(delegate() { HttpPost(URI, Parameters, false, readResponse); });
                thread.Start();
                return "true;threading;";
            }

            WebRequest req = WebRequest.Create(URI);
            req.ContentType = "application/x-www-form-urlencoded";
            req.Method = "POST";
            req.Timeout = settings.http_timeout_sec * 1000;

            byte[] bytes = System.Text.Encoding.UTF8.GetBytes(Parameters);
            Stream os = null;
            try
            {
                req.ContentLength = bytes.Length;
                os = req.GetRequestStream();
                os.Write(bytes, 0, bytes.Length);
            }
            catch (WebException ex)
            {
                rndbuddy.logsilent(ex.Message);
                rndbuddy.logsilent(ex.StackTrace);
                rndbuddy.logsilent(URI);
            }
            finally
            {
                if (os != null)
                {
                    os.Close();
                    os.Dispose();
                }
            }

            //if (!readResponse)
            //    return "true;not_read";

            try
            {
                WebResponse resp = req.GetResponse();
                if (resp == null)
                    return null;

                StreamReader sr = new StreamReader(resp.GetResponseStream());
                string r = sr.ReadToEnd().Trim();
                resp.Dispose();
                req = null; //like dispose *takes cover*
                return r;
            }
            catch (WebException ex)
            {
                rndbuddy.logsilent(ex.Message);
                rndbuddy.logsilent(ex.StackTrace);
                rndbuddy.logsilent(URI);
            }
            
            return "false;error in httpPost";
            
        }

        public static void UploadFile(string url, string fp)
        {
            try
            {
                var myClient = new WebClientEx();
                myClient.Timeout = settings.http_timeout_sec * 1000;
                myClient.UploadFile(url, fp);
                myClient.Dispose();
            }
            catch (Exception e)
            {
                rndbuddy.logcrit("Error in UploadFile");
                rndbuddy.logcrit(e.Message + e.StackTrace);
            }
        }

        public static string getStringFromWeb(string url)
        {
            string s;
            using (WebClient client = new WebClient())
            {
                s = client.DownloadString(url);
                client.Dispose();
            }
            return s;
        }
    }
}
